const path = require('path')
const fs = require('fs')
const parser = require('@babel/parser')
const traverse = require("@babel/traverse").default;
const template = require("@babel/template").default;
const generate = require("@babel/generator");
module.exports = class Complilation{
  constructor(options,compiler){
    this.options = options;
    this.compiler = compiler;
     //E:\web\mywebpack\src\index.js
    this.entry = this.options.entry;
    this.extensions = this.options.resolve?.extensions || ["",".js"];
    this.modules = {};
    // console.log(this.extensions);
  }
  buildModules(){
    this.resolveModules(this.entry);
    return this.modules;
  }
  resolveModules(fileName,relativePath){
    //.js
    const extName = path.extname(fileName);
    const rules = this.options.module.rules.filter(item=>{
      return item.test.test(extName)
    })
    
    let code = fs.readFileSync(fileName);
    // console.log(code)
    if(rules && rules.length){
      const loaders = [];
      rules.forEach(r => {
        loaders.push(...r.use)
      });
      // console.log(loaders)
      code = this.runLoaders(code,fileName,loaders)

      //递归查找所有依赖模块。
      this.analyzeDep(code,fileName,relativePath);
      // console.log(fileName)
    }
  }


  analyzeDep(code,fileName,relativePath){
    const extName = path.extname(fileName);
    
    if(this.extensions.includes(extName)){
      //如果是js文件
      const ast = parser.parse(code,{sourceType:"module"});

      //正在处理那个文件
      this.curTask = {
        ast,
        fileName,
        jsModulePath:relativePath
      };

      const deps = [];
      traverse(ast,{
        CallExpression(path){
          if(path.node.callee.type === "Identifier" && path.node.callee.name === "require"){
            deps.push(path.node.arguments[0].value);
          }
        }
      })
      // console.log(path.join(__dirname,deps[0]))
      // console.log(deps)
      deps.sort(dep=>{
        const extName = path.extname(dep);
        return !extName || this.extensions.includes(extName) ?1:-1;
      })
      const curDir = path.dirname(fileName);
      //this.entry == E:\web\mywebpack\src\index.js

      this.modules[relativePath ? relativePath : fileName] = {
        code,deps,
      }
      
      for (const dep of deps) {
        //E:\web\mywebpack\src\view\dou
        // console.log(path.join(curDir,dep))
        const fileN = this.resolveExtenson(path.join(curDir,dep))
        this.resolveModules(fileN,dep);
        
      }
    }else{
      //如果不是js文件
      const { ast,fileName,jsModulePath} = this.curTask;
      traverse(ast,{
        CallExpression(path){
          if(path.node.callee.type === "Identifier" 
          && path.node.callee.name === "require"
          && path.node.arguments[0].value === relativePath){
            deps.push(path.node.arguments[0].value);
            const distAst = template.ast`
            ${code}
            `;
            path.replaceWith(distAst);
          }
        }
      })

      const result = generate(ast);
      this.modules[jsModulePath ? jsModulePath : fileName] = {
        code,code :result.code
      }
    }
  }

  //为文件加上后缀名.js
  resolveExtenson(filePath){
    for(const ext of this.extensions){
      const fileName  = filePath + ext;
      if(fs.existsSync(fileName))return fileName;
    }
    return filePath;
  }
  runLoaders(code,fileName,loaders){
    // const raw = code;
    // code = code.toString();
    // const loader = loaders.pop();
    // if(loader){
    //   let result;
    //   result =loader.call({fileName,context:this.compiler,raw},code);
      
    //   return this.runLoaders(result,fileName,loader);
    // }
    // return code;


    const raw = code;
    code = code.toString();
    loaders.forEach(item=>{
      code = item.call({fileName,context:this.compiler,raw},code);
    })
    return code;
  }
}